/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.transas.zornav.businesslogic;

import com.transas.navmath.NavMath;
import com.transas.zornav.data.DataSource;
import com.transas.zornav.data.DegMinSecFormatter;
import com.transas.zornav.data.SimpleFormatter;
import com.transas.zornav.data.TimeFormatter;

/**
 *
 * @author amalikov
 */
public class Calculator {
	
	public Calculator() {
		cfg = new Config();
		decimalDegrees = false;
	}

	public void setDecimalDegrees(boolean decimalDegrees) {
		this.decimalDegrees = decimalDegrees;
	}
	
	public void process(DataSource ds) {
		ds.addColumn("Latitude", decimalDegrees ? 
				new SimpleFormatter(10, 10) : new DegMinSecFormatter("N", "S"));	// just changes formatter
		ds.addColumn("Longitude", decimalDegrees ? 
				new SimpleFormatter(10, 10) : new DegMinSecFormatter("E", "W"));	// just changes formatter

		ds.addColumn("Course", new SimpleFormatter(0, 0));
		ds.addColumn("Distance", new SimpleFormatter(2, 2));
		ds.addColumn("Total distance", new SimpleFormatter(2, 2));
		ds.addColumn("GSpeed", new SimpleFormatter(0, 0));
		ds.addColumn("Duration", new TimeFormatter(false));
		ds.addColumn("Total duration", new TimeFormatter(false));
		ds.addColumn("Time", new TimeFormatter(true));
		ds.addColumn("Fuel", new SimpleFormatter(1, 1));

		double totalDist = 0;
		double totalDuration = 0;
		
		for (int i = 0; i < ds.getSize(); ++i) {
			double lat1 = ds.getValue(ds.getHandle("Latitude"), Math.max(0, i - 1));
			double lon1 = ds.getValue(ds.getHandle("Longitude"), Math.max(0, i - 1));
			double lat2 = ds.getValue(ds.getHandle("Latitude"), i);
			double lon2 = ds.getValue(ds.getHandle("Longitude"), i);
			
			double[] distCrs = NavMath.distanceCourse(lat1, lon1, lat2, lon2);
			double dist = distCrs[0];
			double course = distCrs[1];
			totalDist += dist;
			
			double uv = Math.toRadians(cfg.getWindDir() + 180 - course);
			double x = toKmh(cfg.getWindSpeed()) / cfg.getAirSpeed() * Math.sin(uv);
			x = Math.min(Math.max(x, -1.0), 1.0);
			double gspeed = cfg.getAirSpeed() * Math.cos(Math.asin(x)) + toKmh(cfg.getWindSpeed()) * Math.cos(uv);
			gspeed = Math.max(gspeed, 0.1);
			
			double duration = dist / toMps(gspeed);
			totalDuration += duration;
			
			ds.setValue(ds.getHandle("Distance"), i, dist / 1000);
			ds.setValue(ds.getHandle("Course"), i, NavMath.wrapAngle360(course));
			ds.setValue(ds.getHandle("Total distance"), i, totalDist / 1000);
			ds.setValue(ds.getHandle("GSpeed"), i, gspeed);
			ds.setValue(ds.getHandle("Duration"), i, duration);
			ds.setValue(ds.getHandle("Total duration"), i, totalDuration);
			ds.setValue(ds.getHandle("Time"), i, cfg.getDepartureTime() + totalDuration);
			ds.setValue(ds.getHandle("Fuel"), i, Math.max(cfg.getTotalFuel() - (totalDuration / 3600) * cfg.getFuelFlow(), 0));
		}
		
		calculateFuel(ds, totalDuration);
	}
	
	public Config getConfig() {
		return cfg;
	}
	
	private void calculateFuel(DataSource ds, double totalDuration) {
		if (!cfg.isFuelCalculated()) {
			cfg.setTotalFuel((totalDuration / 3600) * cfg.getFuelFlow() * FuelEstimateMul + FuelEstimateConst);
			
			for (int i = 0; i < ds.getSize(); ++i) {
				double t = ds.getValue(ds.getHandle("Total duration"), i);
				ds.setValue(ds.getHandle("Fuel"), i, cfg.getTotalFuel() - (t / 3600) * cfg.getFuelFlow());
			}
			
			cfg.setFuelCalculated(true);
		}
	}
	
	private double toKmh(double value) {
		return value * 3.6;
	}
	
	private double toMps(double value) {
		return value / 3.6;
	}

	private Config cfg;
	private boolean decimalDegrees;	
	
	private final static double FuelEstimateConst = 6.0;
	private final static double FuelEstimateMul = 1.15;
}
